// Package errors 提供自定义错误码及错误处理功能，支持错误码的创建、格式化、
// 错误与错误码的转换，用于统一项目中的错误处理规范。
package errors

import (
	"fmt"
	"io"
	"strconv"
	"strings"
)

// 预定义常见错误码实例，涵盖项目中常用的错误类型
var (
	OK               = NewCode(0, "ok")                // 成功状态，错误码0
	Canceled         = NewCode(1, "canceled")          // 操作被取消，错误码1
	Unknown          = NewCode(2, "unknown")           // 未知错误，错误码2
	InvalidArgument  = NewCode(3, "invalid argument")  // 参数无效，错误码3
	DeadlineExceeded = NewCode(4, "deadline exceeded") // 超时，错误码4
	NotFound         = NewCode(5, "not found")         // 资源不存在，错误码5
	InternalError    = NewCode(6, "internal error")    // 内部错误，错误码6
	Unauthorized     = NewCode(7, "unauthorized")      // 未授权，错误码7
	IllegalInvoke    = NewCode(8, "illegal invoke")    // 非法调用，错误码8
	IllegalRequest   = NewCode(9, "illegal request")   // 非法请求，错误码9
)

// Code 表示错误码类型，包含错误码数字和对应的描述消息
type Code struct {
	code    int    // 错误码数字
	message string // 错误码描述消息（如"ok"、"invalid argument"）
}

// NewCode 创建一个新的错误码实例
// 参数：
//
//	code: 错误码数字（自定义整数）
//	message: 可选参数，错误码的描述消息（若不传入则消息为空）
//
// 返回值：
//
//	*Code: 新创建的错误码实例
func NewCode(code int, message ...string) *Code {
	if len(message) > 0 {
		return &Code{code: code, message: message[0]}
	} else {
		return &Code{code: code} // 无消息时，message为空字符串
	}
}

// Code 返回错误码的数字值
func (c *Code) Code() int {
	return c.code
}

// WithCode 创建一个新的错误码实例，复用当前错误码的消息，替换为新的数字值
// 参数：
//
//	code: 新的错误码数字
//
// 返回值：
//
//	*Code: 新的错误码实例（message与原实例相同）
func (c *Code) WithCode(code int) *Code {
	return &Code{
		code:    code,
		message: c.message,
	}
}

// Message 返回错误码的描述消息
func (c *Code) Message() string {
	return c.message
}

// WithMessage 创建一个新的错误码实例，复用当前错误码的数字值，替换为新的描述消息
// 参数：
//
//	message: 新的描述消息
//
// 返回值：
//
//	*Code: 新的错误码实例（code与原实例相同）
func (c *Code) WithMessage(message string) *Code {
	return &Code{
		code:    c.code,
		message: message,
	}
}

// String 格式化错误码为字符串，格式为"code error: code = {code} desc = {message}"
func (c *Code) String() string {
	return fmt.Sprintf("code error: code = %d desc = %s", c.code, c.message)
}

// Format 实现fmt.Formatter接口，支持自定义格式化输出
// 支持的格式化动词：
//
//	%s: 输出格式为"{code}:{message}"（若message为空则仅输出code）
//	%v: 等价于调用String()方法，输出完整错误码信息
func (c *Code) Format(s fmt.State, verb rune) {
	switch verb {
	case 's': // 处理%s格式化
		if c.message != "" {
			io.WriteString(s, fmt.Sprintf("%d:%s", c.code, c.message))
		} else {
			io.WriteString(s, fmt.Sprintf("%d", c.code))
		}
	case 'v': // 处理%v格式化
		io.WriteString(s, c.String())
	}
}

// Err 将错误码转换为error接口实例（ ErrCode 类型）
// 特殊处理：若当前错误码是OK（code=0），则返回nil（表示无错误）
func (c *Code) Err() error {
	if c.code == OK.Code() {
		return nil
	}

	return &ErrCode{code: c}
}

// ErrCode 是实现了error接口的错误类型，用于包装Code，使错误码可作为error使用
type ErrCode struct {
	code *Code // 被包装的错误码实例
}

// Error 实现error接口，返回错误的字符串描述（复用Code的String()方法）
func (e *ErrCode) Error() string {
	return e.code.String()
}

// Convert 将error类型转换为对应的Code错误码
// 转换逻辑：
//  1. 若error为nil，返回OK（code=0）
//  2. 若error是实现了Code() *Code方法的类型（如ErrCode），直接返回其内部的Code
//  3. 否则解析error的字符串描述（通过Error()方法），提取code和message，创建新的Code
//  4. 若解析失败，返回Unknown（未知错误，code=2）
//
// 函数定义：接收一个error类型参数，返回*Code（Code类型的指针）
func Convert(err error) *Code {
	// 逻辑1：判断错误是否为nil（空错误）
	if err == nil {
		// 若为nil，返回预定义的OK（code=0，通常表示无错误）
		return OK
	}

	// 逻辑2：检查error是否实现了"Code() *Code"方法
	// 语法说明：
	// - 使用类型断言（type assertion）检查err是否匹配匿名接口类型"interface{ Code() *Code }"
	// - 匿名接口定义了一个方法Code()，返回值为*Code
	// - 若断言成功，ok为true，e为匹配该接口的实例；否则ok为false
	if e, ok := err.(interface{ Code() *Code }); ok {
		// 若实现了该方法，直接调用其Code()方法并返回结果（获取内部封装的Code）
		return e.Code()
	}

	// 逻辑3：解析错误字符串（若上述两种情况不满足，则尝试从错误描述中提取信息）
	// 1. 获取error的字符串描述（通过error接口的Error()方法）
	text := err.Error()
	// 定义错误字符串的标志前缀（用于识别可解析的格式）
	flag := "code error:"
	// 查找标志前缀在字符串中的起始索引（strings.Index返回子串首次出现的位置，未找到则返回-1）
	index := strings.Index(text, flag)

	// 若未找到标志前缀，说明格式不匹配，触发逻辑4
	if index == -1 { // 未找到标志字符串，无法解析
		return Unknown // 返回预定义的Unknown（code=2，未知错误）
	}

	// 提取标志前缀后面的部分，并检查是否包含" code = "前缀
	// 语法说明：
	// - strings.CutPrefix(s, prefix)：从s中移除prefix前缀，返回剩余部分和是否移除成功的标志（found）
	// - 这里的起始位置是"index+len(flag)"：跳过前面的"code error:"部分
	after, found := strings.CutPrefix(text[index+len(flag):], " code = ")
	// 若没有" code = "前缀，格式不合法，返回Unknown
	if !found {
		return Unknown
	}

	// 分割字符串，提取code数字部分和后续的描述部分
	// 语法说明：
	// - strings.SplitN(s, sep, n)：将s按sep分割，最多分割n-1次，返回分割后的切片
	// - 这里n=2，表示最多分割成2个元素（[code数字部分, 剩余描述部分]）
	elements := strings.SplitN(after, " ", 2)
	// 若分割后不是2个元素（说明格式错误），返回Unknown
	if len(elements) != 2 {
		return Unknown
	}

	// 解析code数字（elements[0]是code的字符串形式）
	// 语法说明：strconv.Atoi(s)将字符串s转换为int类型，转换失败返回error
	code, err := strconv.Atoi(elements[0])
	// 若转换失败（如elements[0]不是数字），返回Unknown
	if err != nil {
		return Unknown
	}

	// 提取描述信息（从elements[1]中移除"desc = "前缀）
	after, found = strings.CutPrefix(elements[1], "desc = ")
	// 若没有"desc = "前缀，格式不合法，返回Unknown
	if !found {
		return Unknown
	}

	// 逻辑3成功：用解析出的code和message创建新的Code并返回
	return NewCode(code, after)
}
